Skip chromium restart on headful display resize#262
Conversation
Headful PATCH /display previously restarted chromium to re-apply
--start-maximized against the new X root. The restart costs ~9s and
also wipes browser-side state (Emulation.* overrides, devtools
sessions). It turns out the restart isn't load-bearing: mutter reflows
a window in windowState=maximized (or fullscreen) onto the new root
whenever the X root resizes via xrandr/Neko. The server just has to
make sure the window is in that state.
This replaces the restart on the Xorg branch of PatchDisplay with a
single Browser.setWindowBounds{windowState:"maximized"} call. The
helper is idempotent: it early-returns when the window is already
maximized or fullscreen, so kiosk windows stay in fullscreen. Callers
that still want the legacy restart can pass restart_chromium=true.
The explicit-bounds form of setWindowBounds (windowState:"normal" with
{left,top,width,height}) is intentionally NOT used: once a window is
in normal state mutter stops auto-tracking RANDR events, which would
silently break subsequent resizes.
New e2e test e2e_display_resize_window_test.go covers headless fast
path, --start-maximized + neko, kiosk + fullscreen, and a
force-maximized-via-CDP scenario. Each subtest asserts:
- X root reaches the requested size (xrandr Screen 0: current WxH)
- renderer screen.* and outer{Width,Height} converge
- CDP windowState is preserved across the resize
- CDP windowId is stable (proves no chromium restart)
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Follow-up to the prior commit: remove the restart_chromium=true escape hatch from the Xorg branch entirely. The CDP re-assert-maximized path is strictly better (faster, preserves browser state, stable windowId) and the e2e tests exercise it exclusively. Existing callers in kernel/kernel (kraft.go, chromium_configure.go) hardcode restart_chromium=true today and now transparently get the faster behaviour. The restart_chromium field stays in the request schema for API compatibility. On the Xorg branch it is now a no-op. The Xvfb-with- recordings branch (orthogonal — headless chromium has no OS window) still honours the flag. Also drop the unused restartChrome parameter from setResolutionXorgViaXrandr and its one caller in chromium_configure.go. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
After the legacy restart_chromium path was removed, two of the five
TestDisplayResizeChromiumWindow scenarios became strictly redundant:
- headful_kiosk_no_restart: passes restart_chromium=false explicitly,
but the field is a no-op on the Xorg branch — functionally
identical to headful_kiosk.
- headful_force_maximized_no_restart: forces windowState=maximized
via CDP at baseline, then resizes. Was useful while investigating
whether mutter's "maximized window reflows on RANDR" invariant
holds; headful_start_maximized now covers the same hot path with
production-equivalent CHROMIUM_FLAGS.
Both also flaked in CI when running later in a heavy e2e suite —
neko/mutter occasionally fails to apply its 1920x1080 desktop.screen
startup mode under load, leaving the X root at the dummy DDX's
3840x2160 default and breaking the resize assertions. Running them
locally or earlier in the suite passes consistently, so it's an
environmental flake, not a code regression.
Also remove the now-unused helpers setChromiumWindowStateCDP,
waitForWindowState, baselineOrForcedState, and the
forceMaximizeAtBaseline / restartChromium fields on resizeScenario.
The remaining three subtests cover every production path: headless
CDP fast path, headful --start-maximized + neko, headful kiosk +
fullscreen.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Monitoring Plan: Display Resize Skips Chromium Restart, Uses CDP InsteadWhat this PR does: Headful browser display resizes are now ~9 seconds faster and no longer wipe browser-side state (Emulation.* overrides, open devtools sessions). Instead of restarting Chromium after an xrandr resize, the server re-asserts the window's maximized state via a single CDP call — Mutter then automatically reflows the window to fill the new screen. Intended effect:
Risks:
Status updates will be posted automatically on this PR as monitoring progresses. |
Three follow-ups addressing review feedback: 1. xrandr targets DUMMY0 (or KERNEL_IMAGES_XRANDR_OUTPUT env override), not "default". The headful Xorg dummy driver does not expose a "default" output, so `xrandr --output default --mode ...` exits 0 without applying anything. This affects only the non-Neko Xorg path (production runs with Neko enabled, which doesn't touch xrandr), but left the path silently broken. 2. CDP maximize re-assert failure is now fatal. Previously logged a warning and returned 200; if the window happened to be in normal state and the CDP call failed, the server returned success with the browser window stuck at the old size. Now the resize fails closed with a 500 so the caller sees the mismatch. 3. TestDisplayResizeChromiumWindow now asserts windowID stability and windowState preservation across both resizes. Previously logged but never asserted; a silent chromium restart regression would have slipped through. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
The else branch for refreshRate <= 0 built `xrandr --output DUMMY0 --size WxH`. `--size` is xrandr's legacy global screen option (long form of `-s`) and cannot be combined with `--output`; per-output resizing requires `--mode <name>`. The mismatched flag combination either silently no-ops or errors depending on the xrandr version. The branch was effectively dead — the schema enum (PatchDisplayRequestRefreshRate) only permits 10/25/30/60 and getCurrentResolution synthesizes 60 when xrandr is silent — but the footgun should not stay in the tree. Normalize refreshRate to 60 when non-positive and always go through the named modeline. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Two follow-ups against Raf's review: 1. Synchronous post-condition. setWindowMaximizedViaCDP now takes the requested width/height and polls Browser.getWindowForTarget until the live OS-window bounds match. PATCH /display only returns after mutter's RANDR reflow has settled, so callers see a synchronous contract: the response means the window is at the new size, not just that the resize was kicked off. Typical convergence on docker + mutter is 20-50ms; deadline is 3s. Adds cdpclient.GetWindowBounds() returning windowID/width/height/ windowState, plus the waitForWindowSize polling loop in display.go. 2. Non-Neko Xorg coverage. Adds headful_xorg_no_neko subtest with ENABLE_WEBRTC unset, so the server falls through to setResolutionXorgViaXrandr instead of nekoAuthClient.Screen- ConfigurationChange. This actually exercises the `xrandr --output DUMMY0 --mode WxH_RR.00` path the prior commit landed but left untested. Mode targets (1920x1080 / 1280x720) are chosen because they're attached at 60Hz on DUMMY0; 2560x1440 isn't, per `xrandr -q` on this image. Confirms mutter reflows the chromium window onto the new active mode size even though the dummy DDX virtual framebuffer stays at its boot-time maximum. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 42caab4. Configure here.
cdpclient had three near-identical copies of "Target.getTargets → unmarshal → loop for type=page → extract targetId" — SetWindowBoundsMaximized, GetWindowBounds, and SetDeviceMetricsOverride each carried ~20 lines of the same routine. Pulls them into a private firstPageTargetID(ctx) helper. DispatchStartURL has a different shape (walks all page targets, closes extras, falls back to creating one) so it stays as-is. Net 65 lines removed, no behaviour change. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>

Summary
Headful
PATCH /displaypreviously restarted chromium after thexrandr/Neko screen resize so chromium could re-apply
--start-maximizedagainst the new X root. The restart costs ~9s and wipes browser-side
state (Emulation.* overrides, devtools sessions).
The restart turns out not to be load-bearing. Mutter reflows a window in
windowState=maximized(orfullscreen) onto the new root whenever theX root resizes via xrandr/Neko. The server just has to make sure the
window is in that state.
This PR replaces the restart on the Xorg branch with a single
Browser.setWindowBounds{windowState:"maximized"}call via CDP. Therestart_chromiumrequest field stays in the API schema forcompatibility but is now a no-op on the Xorg path. Existing callers
that hardcode
restart_chromium=true(kraft.go, chromium_configure.goin kernel/kernel) silently get the faster path.
The trap we avoid
The explicit-bounds form of
setWindowBounds—windowState:"normal"with
{left, top, width, height}— is intentionally not used. Oncea window is in normal state, mutter stops auto-tracking RANDR events,
which silently breaks subsequent resizes. The new e2e test
headful_force_maximized_no_restartexercises this indirectly by goingnormal → maximized via the helper and confirming reflow still works on
both up- and down-resize.
Changes
server/lib/cdpclient/cdpclient.go—SetWindowBoundsMaximized(ctx).Idempotent: early-returns on
maximizedorfullscreenso kioskwindows aren't demoted.
server/cmd/api/api/display.go— Xorg branch: always callsetWindowMaximizedViaCDPafter a successful resize. The legacyrestart branch is gone.
restartChromeis no longer threaded intosetResolutionXorgViaXrandr.server/cmd/api/api/chromium_configure.go— drop the now-unusedfifth argument from the
setResolutionXorgViaXrandrcall.server/e2e/e2e_display_resize_window_test.go— new test exercisingfive scenarios: headless fast path, headful
--start-maximized+neko, headful kiosk, kiosk with explicit
restart_chromium=false,and force-maximized-via-CDP starting from
normalstate. Eachsubtest asserts X root, renderer
screen.*andouter{Width,Height}convergence,
WindowStatepreservation, and a stablewindowId(proving no chromium restart).
The Xvfb-with-recordings branch in
display.gois orthogonal (headlesschromium has no OS window) and still honours
restart_chromium=true.Numbers
headful_start_maximizedheadful_kioskheadful_kiosk_no_restartheadful_force_maximized_no_restartTwo resizes per scenario; ~17s saved per PATCH
/displaycall againstthe kernel/kernel callers that all hardcode
restart_chromium=truetoday.
Test plan
cd server && go vet ./...cd server && go test ./lib/cdpclient/... ./cmd/api/...— unittests for cdpclient and the api package
TestDisplayResizeChromiumWindow— 5 subtests, all passagainst an overlay image carrying this branch's
kernel-images-apiTestDisplayResolutionChangestill passes against themodified server
the unikernel runtime as in the docker e2e
🤖 Generated with Claude Code
Note
Medium Risk
Changes core headful display behavior and makes PATCH /display depend on CDP/WM timing, but reduces restart risk and adds broad e2e coverage including the previously broken xrandr path.
Overview
Headful
PATCH /displayno longer restarts Chromium after an Xorg resize. After Neko or xrandr changes the X root, the API re-asserts maximized/fullscreen via CDP (Browser.setWindowBounds) and polls until the OS window matches the new size, so responses stay synchronous without ~9s restarts or wiping DevTools/Emulation state.restart_chromiumremains in the schema but is ignored on the Xorg path (Xvfb recording paths unchanged).setResolutionXorgViaXrandrnow targetsDUMMY0(orKERNEL_IMAGES_XRANDR_OUTPUT) withxrandr --output … --mode WxH_RR.00, fixing silent no-ops from--output default/xrandr -s.cdpclientaddsSetWindowBoundsMaximized(idempotent; leaves kiosk fullscreen alone) andGetWindowBounds. New e2eTestDisplayResizeChromiumWindowcovers headless, Neko headful, kiosk, and non-Neko xrandr, asserting stablewindowId, preservedwindowState, and renderer/X root convergence.Reviewed by Cursor Bugbot for commit ea9dd74. Bugbot is set up for automated code reviews on this repo. Configure here.